package com.think.cloud.thinkshop.mall.service.memberuser.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.think.cloud.thinkshop.common.enums.RabbitMessageTypeEnum;
import com.think.cloud.thinkshop.common.enums.operationplan.OperationPlanStatusEnum;
import com.think.cloud.thinkshop.common.enums.operationplan.OperationPlanTypeEnum;
import com.think.cloud.thinkshop.mall.controller.admin.memberuser.vo.*;
import com.think.cloud.thinkshop.mall.convert.memberuser.MemberOperationConvert;
import com.think.cloud.thinkshop.mall.domain.coupon.ProductCoupon;
import com.think.cloud.thinkshop.mall.domain.memberuser.MemberOperationPlan;
import com.think.cloud.thinkshop.mall.domain.memberuser.MemberOperationPlanRef;
import com.think.cloud.thinkshop.mall.mapper.memberuser.MemberOperationPlanMapper;
import com.think.cloud.thinkshop.mall.mapper.memberuser.MemberOperationPlanRefMapper;
import com.think.cloud.thinkshop.mall.rabbit.producer.MessageProducer;
import com.think.cloud.thinkshop.mall.service.coupon.AppProductCouponRelationService;
import com.think.cloud.thinkshop.mall.service.coupon.IProductCouponService;
import com.think.cloud.thinkshop.mall.service.memberuser.IMemberGroupService;
import com.think.cloud.thinkshop.mall.service.memberuser.IMemberOperationPlanService;
import com.think.common.core.exception.ServiceException;
import com.think.common.core.utils.PageUtils;
import com.think.common.core.utils.StringUtils;
import com.think.common.core.web.page.TableDataInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.think.cloud.thinkshop.common.enums.operationplan.OperationPlanRefTypeEnum.*;
import static com.think.common.core.utils.PageUtils.startPage;

/**
 * 客户运营计划Service业务层处理
 *
 * @author zkthink
 * @date 2024-06-07
 */
@Service
@Slf4j
public class MemberOperationPlanServiceImpl implements IMemberOperationPlanService {
    @Autowired
    private MemberOperationPlanMapper memberOperationPlanMapper;
    @Autowired
    private MemberOperationPlanRefMapper memberOperationPlanRefMapper;
    @Autowired
    private IMemberGroupService memberGroupService;
    @Autowired
    private MessageProducer messageProducer;
    @Autowired
    private AppProductCouponRelationService appProductCouponRelationService;
    @Autowired
    private IProductCouponService productCouponService;

    /**
     * 查询客户运营计划
     *
     * @param id 客户运营计划主键
     * @return 客户运营计划
     */
    @Override
    public MemberOperationPlan selectMemberOperationPlanById(Long id) {
        return memberOperationPlanMapper.selectById(id);
    }

    @Override
    public MemberOperationPlanSearchRespVO selectVOById(Long id) {
        MemberOperationPlan memberOperationPlan = memberOperationPlanMapper.selectById(id);
        MemberOperationPlanSearchRespVO vo = MemberOperationConvert.INSTANCE.convert(memberOperationPlan);

        //当前计划的id
        Long planId = vo.getId();
        List<MemberOperationPlanRef> memberOperationPlanRefs = memberOperationPlanRefMapper.selectList(Wrappers.<MemberOperationPlanRef>lambdaQuery()
                .eq(MemberOperationPlanRef::getPlanId, planId)
        );
        List<Long> groupIds = getPlanRefId(memberOperationPlanRefs, 1);
        List<Long> couponIds = getPlanRefId(memberOperationPlanRefs, 2);
        vo.setMemberGroupIds(groupIds);
        vo.setCouponIds(couponIds);

        vo.setMemberGroupList(memberGroupService.selectMemberGroupById(groupIds));
        vo.setCouponList(memberOperationPlanRefMapper.selectCouponList(planId));
        return vo;
    }

    /**
     * 查询客户运营计划列表
     *
     * @param vo 客户运营计划
     * @return 客户运营计划
     */
    @Override
    public TableDataInfo selectMemberOperationPlanList(MemberOperationPlanSearchReqVO vo) {
        //根据 MemberOperationPlanSearchReqVO条件生成查询
        LambdaQueryWrapper<MemberOperationPlan> queryWrapper = buildQueryWrapper(vo);
        startPage();
        List<MemberOperationPlan> memberOperationPlans = memberOperationPlanMapper.selectList(queryWrapper);
        List<MemberOperationPlanSearchRespVO> memberOperationPlansDetail = MemberOperationConvert.INSTANCE.convertList(memberOperationPlans);
        if (ObjectUtil.isEmpty(memberOperationPlansDetail)) {
            return new TableDataInfo(memberOperationPlans, 0);
        }
        List<Long> planIdList = memberOperationPlansDetail.stream().map(MemberOperationPlanSearchRespVO::getId).collect(Collectors.toList());
        List<MemberGroupDetailRespVO> memberGroupList = memberOperationPlanRefMapper.selectGroupList(planIdList);
        Map<Long, List<MemberGroupDetailRespVO>> memberGroupMap = memberGroupList.stream().collect(Collectors.groupingBy(MemberGroupDetailRespVO::getPlanId));

        memberOperationPlansDetail.forEach(item -> {
            item.setMemberGroupList(memberGroupMap.getOrDefault(item.getId(), ListUtil.empty()));
        });
        return new TableDataInfo(memberOperationPlansDetail, PageUtils.getTotal(memberOperationPlans));
    }

    /**
     * 根据 MemberOperationPlanSearchReqVO条件生成查询
     * @param vo /
     * @return /
     */
    private LambdaQueryWrapper<MemberOperationPlan> buildQueryWrapper(MemberOperationPlanSearchReqVO vo) {
        List<Long> planIds = null;
        if (ObjectUtil.isNotNull(vo.getGroup())) {
            planIds = memberOperationPlanRefMapper.selectPlanIdByGroupId(vo.getGroup());
        }
        return Wrappers.<MemberOperationPlan>lambdaQuery()
                .in(ObjectUtil.isNotEmpty(planIds), MemberOperationPlan::getId, planIds)
                .like(StringUtils.isNotBlank(vo.getName()), MemberOperationPlan::getName, vo.getName())
                .eq(vo.getPlanType() != null, MemberOperationPlan::getPlanType, vo.getPlanType())
                .and(ObjectUtil.isNotNull(vo.getStartTime()) && ObjectUtil.isNotNull(vo.getEndTime()), w -> {
                    w.ge(MemberOperationPlan::getPlanStart, vo.getStartTime())
                            .le(MemberOperationPlan::getPlanEnd, vo.getEndTime());
                })
                .orderByDesc(MemberOperationPlan::getCreateTime);
    }

    private List<Long> getPlanRefId(List<MemberOperationPlanRef> memberOperationPlanRefs, int type) {
        return memberOperationPlanRefs.stream()
                .filter(ref -> ref.getRefType() == type)
                .map(MemberOperationPlanRef::getRefId)
                .collect(Collectors.toList());
    }

    /**
     * 新增客户运营计划
     *
     * @param vo 客户运营计划
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertMemberOperationPlan(MemberOperationPlanAddReqVO vo) {
        String name = vo.getName();
        // 校验名称
        judgmentPlanExist(name, null);
        MemberOperationPlan convert = MemberOperationConvert.INSTANCE.convert(vo);
        //校验时间
        checkTime(convert);
        //校验优惠券数据
        checkCoupon(vo);
        int insert = memberOperationPlanMapper.insert(convert);
        if (insert > 0) {
            List<Long> memberGroupIds = vo.getMemberGroupIds();
            List<MemberOperationPlanRef> groupRefs = memberGroupIds.stream().map(id -> {
                MemberOperationPlanRef planRef = new MemberOperationPlanRef();
                planRef.setRefId(id);
                return planRef;
            }).collect(Collectors.toList());

            List<MemberOperationPlanRef> couponRefs = vo.getCouponRefs();
            //当前计划的id
            Long currentPlanId = selectMemberOperationPlanByName(name).getId();
            //插入计划的关联组和关联券
            insertPlanRef(currentPlanId, groupRefs, REF_GROUP.getCode());
            insertPlanRef(currentPlanId, couponRefs, REF_PRODUCT_COUPON.getCode());

            //新建之后应该挂起或立刻执行任务
            messageProducer.sendMessage(JSONUtil.toJsonStr(convert), RabbitMessageTypeEnum.OPERATION_PLAN_EXECUTE);
//            this.doExecute(convert);
        }

        return insert;
    }

    /**
     * 插入计划关系表
     *
     * @param planId    计划id
     * @param targetIds 关联id
     * @param refType   1：人群组,2:优惠券id
     */
    private void insertPlanRef(Long planId, List<MemberOperationPlanRef> targetIds, Integer refType) {
        targetIds.forEach(refId -> {
            refId.setPlanId(planId);
            refId.setRefType(refType);
        });
        Db.saveBatch(targetIds);
    }

    /**
     * 通过计划名称查询计划的条件
     *
     * @param name
     * @return
     */
    private LambdaQueryWrapper<MemberOperationPlan> queryConditionsByName(String name) {
        return Wrappers.<MemberOperationPlan>lambdaQuery().eq(MemberOperationPlan::getName, name);
    }

    /**
     * 判断计划名称是否存在
     *
     * @param name
     * @param id
     */
    private void judgmentPlanExist(String name, Long id) {
        MemberOperationPlan plan = memberOperationPlanMapper.selectOne(
                queryConditionsByName(name)
                        .ne(id != null, MemberOperationPlan::getId, id)
        );
        assert plan != null : new ServiceException("该计划名已存在");
    }

    /**
     * 通过名称查询计划
     *
     * @param name
     * @return
     */
    public MemberOperationPlan selectMemberOperationPlanByName(String name) {
        return memberOperationPlanMapper.selectOne(queryConditionsByName(name));
    }

    /**
     * 修改客户运营计划
     *
     * @param vo 客户运营计划
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateMemberOperationPlan(MemberOperationPlanEditReqVO vo) {
        Long planId = vo.getId();
        MemberOperationPlan plan = selectMemberOperationPlanById(planId);
        if (plan.getStatus() != 0) {
            throw new ServiceException("仅未开始的计划可以编辑");
        }
        judgmentPlanExist(vo.getName(), planId);
        MemberOperationPlan convert = MemberOperationConvert.INSTANCE.convert(vo);
        //校验时间
        checkTime(convert);
        //校验优惠券数据
        checkCoupon(vo);
        int i = memberOperationPlanMapper.updateById(convert);
        if (i > 0) {
            memberOperationPlanRefMapper.delete(
                    Wrappers.<MemberOperationPlanRef>lambdaQuery()
                            .eq(MemberOperationPlanRef::getPlanId, planId)
            );
            List<Long> memberGroupIds = vo.getMemberGroupIds();
            List<MemberOperationPlanRef> groupRefs = memberGroupIds.stream().map(id -> {
                MemberOperationPlanRef planRef = new MemberOperationPlanRef();
                planRef.setRefId(id);
                return planRef;
            }).collect(Collectors.toList());

            List<MemberOperationPlanRef> couponRefs = vo.getCouponRefs();
            //插入计划的关联组和关联券
            insertPlanRef(planId, groupRefs, REF_GROUP.getCode());
            insertPlanRef(planId, couponRefs, REF_PRODUCT_COUPON.getCode());
        }
        return i;
    }

    /**
     * 批量删除客户运营计划信息
     *
     * @param ids 主键集合
     * @return 结果
     */
    @Override
    public void batchDelete(List<Long> ids) {
        for (Long id : ids) {
            MemberOperationPlan plan = selectMemberOperationPlanById(id);
            //进行中是的计划不可删除
            if (plan.getStatus() == 1) {
                continue;
            }
            memberOperationPlanMapper.deleteById(id);
            //删除关联数据
            memberOperationPlanRefMapper.delete(
                    Wrappers.<MemberOperationPlanRef>lambdaQuery()
                            .eq(MemberOperationPlanRef::getPlanId, id)
            );
        }
    }

    public void checkTime(MemberOperationPlan bean) {
        if (bean.getPlanType() == OperationPlanTypeEnum.AUTO.getCode()) {
            //校验开始时间和结束时间
            checkStartEndTime(bean);
        } else if (bean.getPlanType() == OperationPlanTypeEnum.MANUAL.getCode()) {
            if (bean.getManualExecutionType() != 1) {
                //校验开始时间
                checkStartTime(bean);
            }
        }
    }

    private void checkCoupon(MemberOperationPlanAddReqVO vo) {
        //优惠券必须在有效期内，且优惠券有效期必须大于计划结束时间
        if (vo.getPlanType() == OperationPlanTypeEnum.AUTO.getCode()) {
            List<MemberOperationPlanRef> couponRefs = vo.getCouponRefs();
            List<Long> couponIds = couponRefs.stream().map(MemberOperationPlanRef::getRefId).collect(Collectors.toList());
            List<ProductCoupon> couponList = productCouponService.selectProductCouponByIds(couponIds);
            couponList.forEach(coupon -> {
                if (coupon.getStatus() == 2) {
                    throw new ServiceException("优惠券状态异常");
                }
            });
        }
    }


    @Override
    public void execute(MemberOperationPlan bean) {
        if (bean.getPlanType() == OperationPlanTypeEnum.AUTO.getCode()) {
            //发送延时消息
            messageProducer.sendDelayedMessage(JSONUtil.toJsonStr(bean), RabbitMessageTypeEnum.DELAY_OPERATION_PLAN_EXECUTE_AUTO_START, bean.getPlanStart().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        } else if (bean.getPlanType() == OperationPlanTypeEnum.MANUAL.getCode()) {
            if (bean.getManualExecutionType() == 1) {
                this.doExecute(bean);
            } else {
                //发送延时消息
                messageProducer.sendDelayedMessage(JSONUtil.toJsonStr(bean), RabbitMessageTypeEnum.DELAY_OPERATION_PLAN_EXECUTE_MANUAL_START, bean.getPlanStart().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
            }
        }
    }

    @Override
    public void updateStatus(MemberOperationPlan bean, OperationPlanStatusEnum operationPlanStatusEnum) {
        if (operationPlanStatusEnum.getCode().equals(OperationPlanStatusEnum.IN_PROGRESS.getCode())) {
            //执行
            this.doExecute(bean);
        }
        memberOperationPlanMapper.updateStatus(bean.getId(), operationPlanStatusEnum.getCode());
    }

    private static void checkStartTime(MemberOperationPlan bean) {
        if (bean.getPlanStart() == null) {
            throw new ServiceException("计划时间不能为空");
        }
        if (bean.getPlanStart().getTime() < System.currentTimeMillis()) {
            throw new ServiceException("计划开始时间不能小于当前时间");
        }
    }

    private static void checkStartEndTime(MemberOperationPlan bean) {
        if (bean.getPlanStart() == null || bean.getPlanEnd() == null) {
            throw new ServiceException("计划时间不能为空");
        }
        if (bean.getPlanStart().getTime() > bean.getPlanEnd().getTime()) {
            throw new ServiceException("计划结束时间不能小于开始时间");
        }
        if (bean.getPlanStart().getTime() < System.currentTimeMillis()) {
            throw new ServiceException("计划开始时间不能小于当前时间");
        }
    }

    @Override
    public void doExecute(MemberOperationPlan bean) {
        log.info("客户运营计划开始执行：{}", bean.getName());
        //查询当前计划对应的分群
        //查询当前计划对应的优惠券
        List<MemberOperationPlanRef> refList = memberOperationPlanRefMapper.selectList(Wrappers.<MemberOperationPlanRef>lambdaQuery().eq(MemberOperationPlanRef::getPlanId, bean.getId()));
        Map<Boolean, List<MemberOperationPlanRef>> refMap = refList.stream().collect(Collectors.partitioningBy(item -> item.getRefType() == REF_GROUP.getCode()));
        List<MemberOperationPlanRef> memberGroupRefs = refMap.get(Boolean.TRUE);
        List<MemberOperationPlanRef> couponRefs = refMap.get(Boolean.FALSE);
        List<Long> groupIdList = memberGroupRefs.stream().map(MemberOperationPlanRef::getRefId).distinct().collect(Collectors.toList());
        //查询分群对应所有用户
        List<Long> userIdList = memberGroupService.selectUseridListByGroupIds(groupIdList);

        //给对应所有用户发优惠券
        couponRefs.forEach(couponRef -> {
            userIdList.forEach(userId -> {
                try {
                    appProductCouponRelationService.receiveCoupon(couponRef.getRefId(), userId, couponRef.getCouponIssueNum(), bean.getId());
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("发送优惠券失败", e);
                }
            });
        });

        //手动计划 修改状态为完成
        if (bean.getPlanType().equals(OperationPlanTypeEnum.MANUAL.getCode())) {
            memberOperationPlanMapper.updateStatus(bean.getId(), OperationPlanStatusEnum.END.getCode());
        }
        log.info("客户运营计划结束执行：{}", bean.getName());
    }

    @Override
    public void doExecuteAfterAddGroup(Long groupId, Long userId) {
        //查询分群对应的计划
        List<Long> planIdList = memberOperationPlanRefMapper.selectPlanIdByGroupId(groupId);

        planIdList.forEach(planId -> {
            //查询优惠券
            List<MemberOperationPlanRef> couponRefs = memberOperationPlanRefMapper.selectList(Wrappers.<MemberOperationPlanRef>lambdaQuery().eq(MemberOperationPlanRef::getPlanId, planId).eq(MemberOperationPlanRef::getRefType, REF_PRODUCT_COUPON.getCode()));
            //发放优惠券
            couponRefs.forEach(couponRef -> {
                try {
                    appProductCouponRelationService.receiveCoupon(couponRef.getRefId(), userId, couponRef.getCouponIssueNum(), planId);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("发送优惠券失败", e);
                }
            });
        });

    }

    @Override
    public List<MemberOperationPlan> selectPendingList(DateTime dateTime) {
        String yesterday = DateUtil.format(dateTime, "yyyy-MM-dd");
        return memberOperationPlanMapper.selectList(Wrappers.<MemberOperationPlan>query()
                .eq("status", OperationPlanStatusEnum.IN_PROGRESS.getCode())
                .or(wrapper ->
                        wrapper.eq("status", OperationPlanStatusEnum.END.getCode())
                                .eq("DATE_FORMAT(update_time, '%Y-%m-%d')", yesterday)
                )
        );
    }
}
